package org.tablaturehero.services;

import java.util.ArrayList;
import java.util.List;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;
import android.widget.Toast;

public class MusicService extends Service implements OnPreparedListener, OnErrorListener, IMusicService {

	// our media player
	private MediaPlayer mediaPlayer = null;
	
	private MusicServiceBinder binder;
	
	// our AudioFocusHelper object, if it's available (it's available on SDK level >= 8)
	// If not available, this will be null. Always check for null before using!
//	AudioFocusHelper mAudioFocusHelper = null;

	// indicates the state our service:
	enum State {
		Retrieving, // the MediaRetriever is retrieving music
		Stopped, // media player is stopped and not prepared to play
		Preparing, // media player is preparing...
		Playing, // playback active (media player ready!). (but the media player
					// may actually be
		// paused in this state if we don't have audio focus. But we stay in
		// this state
		// so that we know we have to resume playback once we get focus back)
		Paused
		// playback paused (media player ready!)
	};

	private State mState = State.Retrieving;
 
	enum PauseReason {
		UserRequest, // paused by user request
		FocusLoss, // paused because of audio focus loss
	};

	// do we have audio focus?
	enum AudioFocus {
		NoFocusNoDuck, // we don't have audio focus, and can't duck
		NoFocusCanDuck, // we don't have focus, but can play at a low volume 
		Focused // we have full audio focus
	}

	private AudioFocus mAudioFocus = AudioFocus.NoFocusNoDuck;
  
	// Wifi lock that we hold when streaming files from the internet, in order
	// to prevent the
	// device from shutting off the Wifi radio
	private WifiLock mWifiLock;

	// The tag we put on debug messages
	private static final String TAG = "RandomMusicPlayer";

	// These are the Intent actions that we are prepared to handle. Notice that
	// the fact these
	// constants exist in our class is a mere convenience: what really defines
	// the actions our
	// service can handle are the <action> tags in the <intent-filters> tag for
	// our service in
	// AndroidManifest.xml.
	public static final String ACTION_PLAY = "action.PLAY";
	public static final String ACTION_PAUSE = "action.PAUSE";
	public static final String ACTION_STOP = "action.STOP";
//	public static final String ACTION_SKIP = "action.SKIP";
//	public static final String ACTION_REWIND = "action.REWIND";
//	public static final String ACTION_URL = "action.URL";

	public static final String ACTION_GOTO = "action.GOTO";
	
	// The volume we set the media player to when we lose audio focus, but are
	// allowed to reduce
	// the volume instead of stopping playback.
	public static final float DUCK_VOLUME = 0.1f;

	// The ID we use for the notification (the onscreen alert that appears at
	// the notification
	// area at the top of the screen as an icon -- and as text as well if the
	// user expands the
	// notification area).
	static final int NOTIFICATION_ID = 1;

	// Our instance of our MusicRetriever, which handles scanning for media and
	// providing titles and URIs as we need.
//	MusicRetriever mRetriever; 

	/**
	 * Makes sure the media player exists and has been reset. This will create
	 * the media player if needed, or reset the existing media player if one
	 * already exists.
	 */
	void createMediaPlayerIfNeeded() {
		if (mediaPlayer == null) {
			mediaPlayer = new MediaPlayer();

			// Make sure the media player will acquire a wake-lock while
			// playing. If we don't do
			// that, the CPU might go to sleep while the song is playing,
			// causing playback to stop.
			//
			// Remember that to use this, we have to declare the
			// android.permission.WAKE_LOCK
			// permission in AndroidManifest.xml.
			mediaPlayer.setWakeMode(getApplicationContext(),
					PowerManager.PARTIAL_WAKE_LOCK);

			// we want the media player to notify us when it's ready preparing,
			// and when it's done
			// playing:
			mediaPlayer.setOnPreparedListener(this);
//			mediaPlayer.setOnCompletionListener(this);
			mediaPlayer.setOnErrorListener(this);
		} else
			mediaPlayer.reset();
	}

	@Override
	public void onCreate() {
		Log.i(TAG, "debug: Creating service");
		
		binder = new MusicServiceBinder(this); 

		// Create the Wifi lock (this does not acquire the lock, this just
		// creates it)
		mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
				.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");


		// Create the retriever and start an asynchronous task that will prepare
		// it.
//		mRetriever = new MusicRetriever(getContentResolver());
//		(new PrepareMusicRetrieverTask(mRetriever, this)).execute();

		// create the Audio Focus Helper, if the Audio Focus feature is
		// available (SDK 8 or above)
//		if (android.os.Build.VERSION.SDK_INT >= 8)
//			mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(),
//					this);
//		else
//			mAudioFocus = AudioFocus.Focused; // no focus feature, so we always
												// "have" audio focus
	}

	/**
	 * Called when we receive an Intent. When we receive an intent sent to us
	 * via startService(), this is the method that gets called. So here we react
	 * appropriately depending on the Intent's action, which specifies what is
	 * being requested of us.
	 */
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
				
		String action = intent.getAction();
		if (action.equals(ACTION_PLAY))
			processPlayRequest(intent);
		else if (action.equals(ACTION_PAUSE))
			processPauseRequest();
//		else if (action.equals(ACTION_SKIP))
//			processSkipRequest();
		else if (action.equals(ACTION_STOP))
			processStopRequest();
//		else if (action.equals(ACTION_REWIND))
//			processRewindRequest();
//		else if (action.equals(ACTION_URL))
//			processAddRequest(intent);
		else if (action.equals(ACTION_GOTO))
			processGotoRequest(intent);

		return START_NOT_STICKY; // Means we started the service, but don't wantit to restart in case it's killed.
	}

	private void processGotoRequest(Intent intent) {
		
		int progress = intent.getIntExtra("PROGRESS", 0);
		mediaPlayer.seekTo( (mediaPlayer.getDuration()*progress) /100 );
		if (mState == State.Paused) {
			mediaPlayer.start();
			mState = State.Playing;
		}
		
	}

	/**
	 * HCA : let's play a song !
	 */
	void processPlayRequest(Intent intent) {
//		if (mState == State.Retrieving) {
//			// If we are still retrieving media, just set the flag to start
//			// playing when we're
//			// ready
//			mWhatToPlayAfterRetrieve = null; // play a random song
//			mStartPlayingAfterRetrieve = true;
//			return;
//		}
//
////		tryToGetAudioFocus();
//
//		if (mState == State.Stopped) {
//			// If we're stopped, just go ahead to the next song and start
//			// playing
//			playNextSong(null);
//		} else if (mState == State.Paused) {
//			// If we're paused, just continue playback and restore the
//			// 'foreground service' state.
//			mState = State.Playing;
////			setUpAsForeground(mSongTitle + " (playing)");
//			configAndStartMediaPlayer();
//		}
		
		if (mState != State.Paused) {
			mediaPlayer = MediaPlayer.create(getApplicationContext(), org.tablaturehero.R.raw.test);
		}
		
		mediaPlayer.start();
		mediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener()  {
			
			@Override
			public void onBufferingUpdate(MediaPlayer mp, int percent) {
				
				//notify the listener in order to move the seekbar
				fireDataChanged((mp.getCurrentPosition()*100)/mp.getDuration());
			}
		});
		mState = State.Playing;
	}

	void processPauseRequest() {

		if (mState == State.Playing) {
			// Pause media player and cancel the 'foreground service' state.
			mState = State.Paused;
			mediaPlayer.pause();
			relaxResources(false); // while paused, we always retain the
									// MediaPlayer
//			giveUpAudioFocus();
		}
	}

//	void processRewindRequest() {
//		if (mState == State.Playing || mState == State.Paused)
//			mediaPlayer.seekTo(0);
//	}

//	void processSkipRequest() {
//		if (mState == State.Playing || mState == State.Paused) {
//			tryToGetAudioFocus();
//			playNextSong(null);
//		}
//	}

	void processStopRequest() {
		
		if (mState == State.Playing || mState == State.Paused) {
			
			mState = State.Stopped;

			// let go of all resources...
			relaxResources(true);
//			giveUpAudioFocus();

			// service is no longer necessary. Will be started again if needed.
			stopSelf();
		}
	}

	/**
	 * Releases resources used by the service for playback. This includes the
	 * "foreground service" status and notification, the wake locks and possibly
	 * the MediaPlayer.
	 * 
	 * @param releaseMediaPlayer
	 *            Indicates whether the Media Player should also be released or
	 *            not
	 */
	void relaxResources(boolean releaseMediaPlayer) {
		// stop being a foreground service
		stopForeground(true);

		// stop and release the Media Player, if it's available
		if (releaseMediaPlayer && mediaPlayer != null) {
			mediaPlayer.reset();
			mediaPlayer.release();
			mediaPlayer = null;
		}

		// we can also release the Wifi lock, if we're holding it
		if (mWifiLock.isHeld())
			mWifiLock.release();
	}

//	void giveUpAudioFocus() {
//		if (mAudioFocus == AudioFocus.Focused && mAudioFocusHelper != null
//				&& mAudioFocusHelper.abandonFocus())
//			mAudioFocus = AudioFocus.NoFocusNoDuck;
//	}

	/**
	 * Reconfigures MediaPlayer according to audio focus settings and
	 * starts/restarts it. This method starts/restarts the MediaPlayer
	 * respecting the current audio focus state. So if we have focus, it will
	 * play normally; if we don't have focus, it will either leave the
	 * MediaPlayer paused or set it to a low volume, depending on what is
	 * allowed by the current focus settings. This method assumes mediaPlayer !=
	 * null, so if you are calling it, you have to do so from a context where
	 * you are sure this is the case.
	 */
	void configAndStartMediaPlayer() {
		if (mAudioFocus == AudioFocus.NoFocusNoDuck) {
			// If we don't have audio focus and can't duck, we have to pause,
			// even if mState
			// is State.Playing. But we stay in the Playing state so that we
			// know we have to resume
			// playback once we get the focus back.
			if (mediaPlayer.isPlaying())
				mediaPlayer.pause();
			return;
		} else if (mAudioFocus == AudioFocus.NoFocusCanDuck)
			mediaPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME); // we'll be relatively
															// quiet
		else
			mediaPlayer.setVolume(1.0f, 1.0f); // we can be loud

		if (!mediaPlayer.isPlaying())
			mediaPlayer.start();
	}
//
//	void processAddRequest(Intent intent) {
//		// user wants to play a song directly by URL or path. The URL or path
//		// comes in the "data"
//		// part of the Intent. This Intent is sent by {@link MainActivity} after
//		// the user
//		// specifies the URL/path via an alert box.
//		if (mState == State.Retrieving) {
//			// we'll play the requested URL right after we 
//
//
//		} else if (mState == State.Playing || mState == State.Paused
//				|| mState == State.Stopped) {
//			Log.i(TAG, "Playing from URL/path: " + intent.getData().toString());
////			tryToGetAudioFocus(); 
//		}
//	}

//	void tryToGetAudioFocus() {
//		if (mAudioFocus != AudioFocus.Focused && mAudioFocusHelper != null
//				&& mAudioFocusHelper.requestFocus())
//			mAudioFocus = AudioFocus.Focused;
//	}

//	/**
//	 * Starts playing the next song. If manualUrl is null, the next song will be
//	 * randomly selected from our Media Retriever (that is, it will be a random
//	 * song in the user's device). If manualUrl is non-null, then it specifies
//	 * the URL or path to the song that will be played next.
//	 */
//	void playNextSong(String manualUrl) {
//		mState = State.Stopped;
//		relaxResources(false); // release everything except MediaPlayer
//
//		try {
//			if (manualUrl != null) {
//				// set the source of the media player to a manual URL or path
//				createMediaPlayerIfNeeded();
//				mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//				mediaPlayer.setDataSource(manualUrl);
//				mSongTitle = manualUrl;
//				mIsStreaming = manualUrl.startsWith("http:")
//						|| manualUrl.startsWith("https:");
//			} else {
//				mIsStreaming = false; // playing a locally available song
//
////				MusicRetriever.Item item = mRetriever.getRandomItem();
////				if (item == null) {
////					say("No song to play :-(");
////					return;
////				}
//
//				// set the source of the media player a a content URI
//				createMediaPlayerIfNeeded();
//				mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
////				mediaPlayer.setDataSource(getApplicationContext(), item.getURI());
////				mSongTitle = item.getTitle();
//			}
//
//			mState = State.Preparing;
////			setUpAsForeground(mSongTitle + " (loading)");
//
//			// starts preparing the media player in the background. When it's
//			// done, it will call
//			// our OnPreparedListener (that is, the onPrepared() method on this
//			// class, since we set
//			// the listener to 'this').
//			//
//			// Until the media player is prepared, we *cannot* call start() on
//			// it!
//			mediaPlayer.prepareAsync();
//
//			// If we are streaming from the internet, we want to hold a Wifi
//			// lock, which prevents
//			// the Wifi radio from going to sleep while the song is playing. If,
//			// on the other hand,
//			// we are *not* streaming, we want to release the lock if we were
//			// holding it before.
//			if (mIsStreaming)
//				mWifiLock.acquire();
//			else if (mWifiLock.isHeld())
//				mWifiLock.release();
//		} catch (IOException ex) {
//			Log.e("MusicService",
//					"IOException playing next song: " + ex.getMessage());
//			ex.printStackTrace();
//		}
//	}

	/** Called when media player is done preparing. */
	@Override
	public void onPrepared(MediaPlayer player) {
		// The media player is done preparing. That means we can start playing!
		mState = State.Playing;
//		updateNotification(mSongTitle + " (playing)");
		configAndStartMediaPlayer();
	}
	
	/**
	 * Called when there's an error playing media. When this happens, the media
	 * player goes to the Error state. We warn the user about the error and
	 * reset the media player.
	 */
	@Override
	public boolean onError(MediaPlayer mp, int what, int extra) {
		Toast.makeText(getApplicationContext(),
				"Media player error! Resetting.", Toast.LENGTH_SHORT).show();
		Log.e(TAG, "Error: what=" + what + ", extra="+ extra);

		mState = State.Stopped;
		relaxResources(true);
//		giveUpAudioFocus();
		return true; // true indicates we handled the error
	}

	@Override
	public void onDestroy() {
		
		binder = null;
		// Service is being killed, so make sure we release our resources
		mState = State.Stopped;
		relaxResources(true);
//		giveUpAudioFocus();
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		return binder;
	}

	private List<IMusicServiceListener> listeners = null; 
	 
	// Ajout d'un listener 
	public void addListener(IMusicServiceListener listener) { 
	    if(listeners == null){ 
	        listeners = new ArrayList<IMusicServiceListener>(); 
	    } 
	    listeners.add(listener); 
	} 
	 
	// Suppression d'un listener 
	public void removeListener(IMusicServiceListener listener) { 
	    if(listeners != null){ 
	        listeners.remove(listener); 
	    } 
	} 
	 
	// Notification des listeners 
	private void fireDataChanged(Integer percentage){ 
	    if(listeners != null){ 
	        for(IMusicServiceListener listener: listeners){ 
	            listener.musicProgressionChanged(percentage); 
	        } 
	    } 
	}
	
}